package org.monkey.socket.plugin.config;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.monkey.socket.plugin.entity.WebSocketProperties;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;


@Slf4j
public class NioWebSocketServer implements InitializingBean, DisposableBean {

  private WebSocketProperties webSocketProperties;
  private NioWebSocketChannelInitializer webSocketChannelInitializer;

  public NioWebSocketServer(WebSocketProperties webSocketProperties,
      NioWebSocketChannelInitializer webSocketChannelInitializer) {
    this.webSocketProperties = webSocketProperties;
    this.webSocketChannelInitializer = webSocketChannelInitializer;
  }

  private EventLoopGroup bossGroup;
  private EventLoopGroup workGroup;
  private ChannelFuture channelFuture;

  @Override
  public void afterPropertiesSet() throws Exception {
    try {
      bossGroup = new NioEventLoopGroup(webSocketProperties.getBoss());
      workGroup = new NioEventLoopGroup(webSocketProperties.getWork());

      ServerBootstrap serverBootstrap = new ServerBootstrap();
      serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
          .group(bossGroup, workGroup)
          .channel(NioServerSocketChannel.class)
          .localAddress(webSocketProperties.getPort())
          .childHandler(webSocketChannelInitializer);

      channelFuture = serverBootstrap.bind().sync();
    } finally {
      if (channelFuture != null && channelFuture.isSuccess()) {
        log.info("Netty server startup on port: {} (websocket) with context path '{}'",
            webSocketProperties.getPort(), webSocketProperties.getPath());
      } else {
        log.error("Netty server startup failed.");
        if (bossGroup != null) {
          bossGroup.shutdownGracefully().sync();
        }
        if (workGroup != null) {
          workGroup.shutdownGracefully().sync();
        }
      }
    }
  }

  @Override
  public void destroy() throws Exception {
    log.info("Shutting down Netty server...");
    if (bossGroup != null && !bossGroup.isShuttingDown() && !bossGroup.isTerminated()) {
      bossGroup.shutdownGracefully().sync();
    }
    if (workGroup != null && !workGroup.isShuttingDown() && !workGroup.isTerminated()) {
      workGroup.shutdownGracefully().sync();
    }
    if (channelFuture != null && channelFuture.isSuccess()) {
      channelFuture.channel().closeFuture().sync();
    }
    log.info("Netty server shutdown.");
  }
}
